/** ==================================================================================== **/
/**   Estensioni di proprietà e/o metodi delle classi interne ad openlayers 3 (ol.js).   **/
/** ==================================================================================== **/


/********************************************************/
/*                                                      */
/*          Extended ol.Geolocation                     */
/*                                                      */
/*       Aggiunto l'attributo "geolocateZoom" e i       */
/*       metodi set e get.								*/
/*                                                      */
/********************************************************/    

/**
 * Set del livello di zoom on class ol.Geolocation
 * @param {number}
 */
ol.Geolocation.prototype.setGeolocateZoom = function(geolocateZoom) {
  this.set(ol.ExtGeolocationProperty.GEOLOCATE_ZOOM, geolocateZoom);
};

/**
 * Ritorna il livello di zoom al quale portare la mappa quando viene geolocalizzata la posizione dell'utente. 
 * @return {number}
 */
ol.Geolocation.prototype.getGeolocateZoom = function() {
  return (this.get(ol.ExtGeolocationProperty.GEOLOCATE_ZOOM));
};

/********************************************************/
/*                                                      */
/*           Extended ol.layer.Base                     */
/*                                                      */
/*   Aggiunti gli attributi "id" e "name" e i metodi    */
/*   set e get agli attributi di base già previsti.     */
/*                                                      */
/********************************************************/

/**
 * Ritorna l'identificativo del layer.
 * @return {String}
 */
ol.layer.Base.prototype.getId = function() {
  return (this.get(ol.ExtLayerBaseProperty.ID));
};

/**
 * Set id on class ol.layer.Base
 * @param {String}
 */
ol.layer.Base.prototype.setId = function(id) {
  this.set(ol.ExtLayerBaseProperty.ID, id);
};

/**
 * Ritorna il nome del layer.
 * @return {String}
 */
ol.layer.Base.prototype.getName = function() {
  return (this.get(ol.ExtLayerBaseProperty.NAME));
};

/**
 * Set del nome on class ol.layer.Base
 * @param {String}
 */
ol.layer.Base.prototype.setName = function(name) {
  this.set(ol.ExtLayerBaseProperty.NAME, name);
};

/********************************************************/
/*                                                      */
/*                    Map Functions                     */
/*                                                      */
/*   The following functions deal with get layers map   */
/*                                                      */
/********************************************************/ 

/**
 * Ritorna le opzioni di configurazione dalle quali la mappa è stata costruita.
 * @return {String}
 */
ol.Map.prototype.getOptions = function() {
  return (this.get(ol.ExtMapProperty.OPTIONS));
};

/**
 * Set del nome on class ol.layer.Base
 * @param {String}
 */
ol.Map.prototype.setOptions = function(options) {
  this.set(ol.ExtMapProperty.OPTIONS, options);
};

/**
 * APIMethod: getLayersByName
 * Ottenere il layer con nome corrispondente al nome dato.
 *
 * Parametri:
 *   match - {String | Object} Nome del layer.  
 *  
 * Se non vengono trovati layer, viene restituito null.
 *
 * Returns:
 * {ol.layer.Base} 
 * Il layer corrispondente al nome dato. Viene restituito null se non vengono trovate corrispondenze.
 */
ol.Map.prototype.getLayerByName = function(match) {
	if(!match)
		return null;
     
  	var test = (typeof match.test == "function");
	var layers = this.getLayerGroup().getLayers();
	
	var found = null;
	  
	layers.forEach(function(layer, i) {
        if(layer.getName() == match || (test && match.test(layer.getName()))){
        	found = layer;
        };
    });
    return found; 
};

/**
 * APIMethod: getLayersById
 * Ottenere il layer con identificativo corrispondente all 'id dato.
 *
 * Parametri:
 *   match - {String | Object} Identificativo del layer.  
 *  
 * Se non vengono trovati layer, viene restituito null.
 *
 * Returns:
 * {ol.layer.Base} 
 * Il layer corrispondente all'identificativo dato. Viene restituito null se non vengono trovate corrispondenze.
 */
ol.Map.prototype.getLayerById = function(match) {
	var test = (typeof match.test == "function");
     
	var layers = this.getLayerGroup().getLayers();
	
	var found = null;
	  
	layers.forEach(function(layer, i) {
        if(layer.getId() == match || (test && match.test(layer.getId()))){
        	found = layer;
        };
    });
    return found; 
};

/**
 * APIMethod: getNumLayers
 * 
 * Returns:
 *  {Int} Numero di layer aggiunti alla mappa.
 */
ol.Map.prototype.getNumLayers = function () {
	var layers = this.getLayerGroup().getLayers();
	
	return (layers != undefined && layers != null ) ? layers.getLength() : 0;
};

/**
 * APIMethod: addLayers 
 *
 * Parameters:
 *  layers - {Array(<ol.layer.BaseLayer>)} 
 */    
ol.Map.prototype.addLayers = function (layers) {
  for (var i=0, len=layers.length; i<len; i++) {
      this.addLayer(layers[i]);
  }
};

/**
 * APIMethod: addControls 
 *
 * Parameters:
 *  layers - {Array(<ol.control.Control>)} 
 */    
ol.Map.prototype.addControls = function (controls) {
  if(controls != undefined && controls != null && controls.length != 0)
	  for (var i = 0, len = controls.length; i < len; i++) {
	      this.addControl(controls[i]);
	  }
};

/********************************************************/
/*                                                      */
/*              Extended ol.control.Control             */
/*                                                      */
/*   Aggiunti gli attributi "id" e "name" e i metodi    */
/*   set e get agli attributi di base già previsti.     */
/*                                                      */
/********************************************************/

/**
 * Ritorna l'identificativo del controllo.
 * @return {String}
 */
ol.control.Control.prototype.getId = function() {
  return (this.get(ol.ExtControlProperty.ID));
};

/**
 * Set id on class ol.control.Control
 * @param {String}
 */
ol.control.Control.prototype.setId = function(id) {
  this.set(ol.ExtControlProperty.ID, id);
};

/**
 * Ritorna il nome del controllo.
 * @return {String}
 */
ol.control.Control.prototype.getName = function() {
  return (this.get(ol.ExtControlProperty.NAME));
};

/**
 * Set name on class ol.control.Control
 * @param {String}
 */
ol.control.Control.prototype.setName = function(name) {
  this.set(ol.ExtControlProperty.NAME, name);
};

/********************************************************/
/*                                                      */
/*                 Control Functions                    */
/*                                                      */
/*  The following functions deal with get Controls      */
/*					from the Map   					    */
/*                                                      */
/********************************************************/

/**
 * APIMethod: getControlById
 * Ritorna il controllo dove l'identificativo (ID) è uguale a quello dato.
 *
 * Parameters:
 *    match - {String} A control ID.
 *
 * Returns:
 * {ol.control.Control} 
 * Il controllo corrispondente all'identificativo dato. Viene restituito null se non vengono trovate corrispondenze.
 */
ol.Map.prototype.getControlById = function(match) {
	var controls = this.getControls();
	
	var found = null;
	  
	controls.forEach(function(control, i) {
        if(control.getId() == match){
        	found = control;
        };
    });
    return found; 
};


/********************************************************/
/*                                                      */
/*       Extended ol.interaction.Interaction            */
/*                                                      */
/*   Aggiunti gli attributi "id" e "name" e i metodi    */
/*   set e get agli attributi di base già previsti.     */
/*                                                      */
/********************************************************/

/**
 * Ritorna l'identificativo dell'interaction.
 * @return {String}
 */
ol.interaction.Interaction.prototype.getId = function() {
  return (this.get(ol.ExtInteractionProperty.ID));
};

/**
 * Set id on class ol.interaction.Interaction
 * @param {String}
 */
ol.interaction.Interaction.prototype.setId = function(id) {
  this.set(ol.ExtInteractionProperty.ID, id);
};

/**
 * Ritorna il nome dell'interaction.
 * @return {String}
 */
ol.interaction.Interaction.prototype.getName = function() {
  return (this.get(ol.ExtInteractionProperty.NAME));
};

/**
 * Set name on class ol.interaction.Interaction
 * @param {String}
 */
ol.interaction.Interaction.prototype.setName = function(name) {
  this.set(ol.ExtInteractionProperty.NAME, name);
};

/********************************************************/
/*                                                      */
/*                Interaction Functions                 */
/*                                                      */
/*  The following functions deal with get Interactions  */
/*					from the Map   					    */
/*                                                      */
/********************************************************/

/**
 * APIMethod: getInteractionById
 * Ritorna l'interaction dove l'identificativo (ID) è uguale a quello dato.
 *
 * Parameters:
 *    match - {String} An interaction ID.
 *
 * Returns:
 * {ol.interaction.Interaction} 
 * L'interaction corrispondente all'identificativo dato. Viene restituito null se non vengono trovate corrispondenze.
 */
ol.Map.prototype.getInteractionById = function(match) {
	var interactions = this.getInteractions();
	
	var found = null;
	  
	interactions.forEach(function(interaction, i) {
        if(interaction.getId() == match){
        	found = interaction;
        };
    });
    return found; 
};

/**
 * APIMethod: setCursor
 *  
 * Attiva per la "ol.interaction.Select" la gestione del cursore all'hover sulla feature.
 */
ol.interaction.Select.prototype.setCursor = function() {
	var self = this;
	
	/**
	 * @type {string|undefined}
	 * @private
	 */
	this.previousCursor_ = undefined;
	/**
	 * @type {string|undefined}
	 * @private
	 */
	this.cursor_ = 'pointer';
      
	/**
	 * Metodo chiamato dalla mappa per notificare all'interaction che un evento browser è stato inviato alla mappa. La funzione può restituire 
	 * false per evitare la propagazione dell'evento per le altre interazioni nella catena d'interazioni della mappa.
	 */
	ol.interaction.Interaction.call(this, {	
		handleEvent: ol.interaction.Select.prototype.handleMove_ 
	});
};

/**
 * Funzione richiamata quanto eè scatenato l'evento "pointermove", cioè quando il puntatore si sposta sulla mappa.
 * Quando il puntatore passa su una feature assume valore css 'pointer', mentre se il puntatore passa su un elemento non feature assume
 * valore css 'default'.
 * 
 * @param {ol.event} e "move" event
 */
ol.interaction.Select.prototype.handleMove_ = function(evt) {
	if (evt.type == "pointermove" && this.cursor_) {
		/* get instance map */
        var map = evt.map;
    
        /*
         * Rilevare se vi è una feature che interseca il punto nella finestra della mappa. 
         */
        var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
              return feature;
        });
        
        /* get container map */
        var element = map.getTargetElement();
        
        if(feature) {
        	/* Ha trovato una feature cambio il cursore in pointer */
        	if(element.style.cursor != this.cursor_) {
        		this.previousCursor_ = element.style.cursor;
        		element.style.cursor = this.cursor_;
        	}
        }else 
        	/* Non Ha trovato una feature cambio il cursore nel default */
        	if(this.previousCursor_ !== undefined) {
        		element.style.cursor = this.previousCursor_;
        		this.previousCursor_ = undefined;
	        }
	}
	
	return true;
};


/********************************************************/
/*                                                      */
/*       			Extended ol.Overlay   		        */
/*                                                      */
/*   Aggiunti gli attributi "id" e i metodi   			*/
/*   set e get agli attributi di base già previsti.     */
/*                                                      */
/********************************************************/

/**
 * Ritorna l'identificativo dell'interaction.
 * @return {String}
 */
ol.Overlay.prototype.getId = function() {
  return (this.get(ol.ExtOverlayProperty.ID));
};

/**
 * Set id on class ol.Overlay
 * @param {String}
 */
ol.Overlay.prototype.setId = function(id) {
  this.set(ol.ExtOverlayProperty.ID, id);
};

/********************************************************/
/*                                                      */
/*               	 Overlay Functions                  */
/*                                                      */
/*  The following functions deal with get Overlay  		*/
/*					from the Map   					    */
/*                                                      */
/********************************************************/

/**
 * APIMethod: getOverlayById
 * Ritorna l'overlay dove l'identificativo (ID) è uguale a quello dato.
 *
 * Parameters:
 *    match - {String} An overlay ID.
 *
 * Returns:
 * {ol.Overlay} 
 * L'overlay corrispondente all'identificativo dato. Viene restituito null se non vengono trovate corrispondenze.
 */
ol.Map.prototype.getOverlayById = function(match) {
	var overlays = this.getOverlays();
	
	var found = null;
	  
	overlays.forEach(function(overlay, i) {
        if(overlay.getId() == match){
        	found = overlay;
        };
    });
    return found; 
};

/********************************************************/
/*                                                      */
/*           Extended ol.source.Vector                  */
/*                                                      */
/********************************************************/

/**
 * APIMethod: getFeaturesByAttribute
 *
 * Restituisce un array di feature che hanno la chiave di attributo data impostato al valore dato. 
 * Il confronto dei valori degli attributi considera i tipi di dati, ad esempio, la stringa '1234' non è uguale al numero 1234.
 * 
 * Parametri:
 * 	 attrName - {String}
 * 	 attrValue - {Mixed}
 *
 * Return:
 *    Array({<ol.Feature>}) Un array di feature che hanno l'attributo passato impostato al valore dato.
 */
ol.source.Vector.prototype.getFeaturesByAttribute = function(attrName, attrValue) {
	
    var features = this.getFeatures(), foundFeatures = [];
    
    if(features != null && features.length != 0){
    	
    	for(var i = 0; i < features.length; i++) {
    		var feature = features[i];
	        if(feature && feature.getProperties()) {
	            if (feature.getProperties()[attrName] === attrValue) {
	                foundFeatures.push(feature);
	            }
	        }
    	}
    }
    
    return foundFeatures;
};


/********************************************************/
/*                                                      */
/*           	    Extended ol.Feature                 */
/*                                                      */
/********************************************************/

/**
 * Ritorna il layer a cui è stata aggiunta la feature fonita come parametro.
 * @param {ol.Map} map.
 * @param {ol.Feature} feature
 * @return {ol.layer.Vector} Layer.
 */
ol.Feature.prototype.getLayer = function(map, feature) {
    var this_ = this, layer_, layersToLookFor = [];
    /**
     * Populates array layersToLookFor with only
     * layers that have features
     */
    var check = function(layer){
        var source = layer.getSource();
        if(source instanceof ol.source.Vector){
            var features = source.getFeatures();
            if(features.length > 0){
                layersToLookFor.push({
                    layer: layer,
                    features: features
                });
            }
        }
    };
    //loop through map layers
    map.getLayers().forEach(function(layer){
        if (layer instanceof ol.layer.Group) {
            layer.getLayers().forEach(check);
        } else {
            check(layer);
        }
    });
    layersToLookFor.forEach(function(obj){
        var found = obj.features.some(function(feature){
            return this_ === feature;
        });
        if(found){
            //this is the layer we want
            layer_ = obj.layer;
        }
    });
    return layer_;
};